package belf.migrate.api.table.schema;


import belf.migrate.api.exception.*;
import belf.migrate.api.model.*;
import belf.migrate.api.taskconf.TaskConf;
import belf.migrate.api.util.CatalogUtils;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.List;

/**
 * Interface for reading and writing table metadata from belf. Each connector need to contain
 * the implementation of Catalog.
 */
public interface Catalog extends AutoCloseable {

    /**
     * Open the catalog. Used for any required preparation in initialization phase.
     *
     * @throws CatalogException in case of any runtime exception
     */
    void open() throws CatalogException;

    /**
     * Close the catalog when it is no longer needed and release any resource that it might be
     * holding.
     *
     * @throws CatalogException in case of any runtime exception
     */
    @Override
    void close() throws CatalogException;

    /** Get the name of the catalog. */
    String name();

    // --------------------------------------------------------------------------------------------
    // database
    // --------------------------------------------------------------------------------------------

    /**
     * Get the names of all databases in this catalog.
     *
     * @return a list of the names of all databases
     * @throws CatalogException in case of any runtime exception
     */
    List<String> listDatabases() throws CatalogException;

    /**
     * Get the names of all catalog in this database
     *
     * @return a list of the names of all databases
     * @throws CatalogException in case of any runtime exception
     */
    List<String> listCatalogs() throws CatalogException;


    /**
     * Get the names of all schema by databaseName in this catalog.
     *
     * @return a list of the names of all schemas
     * @throws CatalogException in case of any runtime exception
     */
    List<String> listSchemasByDatabase(String databaseName) throws CatalogException;

    /**
     * Get the name of the default database for this catalog. The default database will be the
     * current database for the catalog when user's session doesn't specify a current database. The
     * value probably comes from configuration, will not change for the life time of the catalog
     * instance.
     *
     * @return the name of the current database
     * @throws CatalogException in case of any runtime exception
     */
    String getDefaultDatabase() throws CatalogException;

    /**
     * Check if a database exists in this catalog.
     *
     * @param databaseName Name of the database
     * @return true if the given database exists in the catalog false otherwise
     * @throws CatalogException in case of any runtime exception
     */
    boolean databaseExists(String databaseName) throws CatalogException;

    // --------------------------------------------------------------------------------------------
    // table
    // --------------------------------------------------------------------------------------------

    /**
     * Get names of all tables under this database. An empty list is returned if none exists.
     *
     * @return a list of the names of all tables in this database
     * @throws CatalogException in case of any runtime exception
     */
    List<String> listTables(String databaseName, String schemaName) throws CatalogException, DatabaseNotExistException;

    /**
     * 获取表的信息
     */
    List<TableInfoModel> listTablesInfo(String databaseName, String schemaName) throws CatalogException, DatabaseNotExistException;

    /**
     * 根据表名获取表的信息
     */
    TableInfoModel getTableInfoByName(TablePath tablePath) throws CatalogException;

    /**
     * Check if a table exist in this catalog.
     *
     * @param tablePath Path of the table
     * @return true if the given table exists in the catalog false otherwise
     * @throws CatalogException in case of any runtime exception
     */
    boolean tableExists(TablePath tablePath) throws CatalogException;

    /**
     * Check if a function exist in this catalog.
     *
     */
    boolean functionExists(String databaseName, String schemaName, String functionName) throws CatalogException;

    /**
     * Check if a view exist in this catalog.
     *
     */
    boolean viewExists(String databaseName, String schemaName, String viewName) throws CatalogException;

    /**
     * Check if a trigger exist in this catalog.
     *
     */
    boolean triggerExists(String databaseName, String schemaName, String triggerName) throws CatalogException;

    /**
     * Check if a procedure exist in this catalog.
     *
     */
    boolean procedureExists(String databaseName, String schemaName, String procedureName) throws CatalogException;

    /**
     * Check if a type exist in this catalog.
     *
     */
    boolean typeExists(String databaseName, String schemaName, String typeName) throws CatalogException;

    /**
     * Check if a sequence exist in this catalog.
     *
     */
    boolean sequenceExists(String databaseName, String schemaName, String sequenceName) throws CatalogException;

    /**
     * Return a {@link CatalogTable} identified by the given {@link TablePath}. The framework will
     * resolve the metadata objects when necessary.
     *
     * @param tablePath Path of the table
     * @return The requested table
     * @throws CatalogException in case of any runtime exception
     */
    CatalogTable getTable(TablePath tablePath) throws CatalogException, TableNotExistException;

    /**
     * Return a {@link CatalogTable} identified by the given {@link TablePath} and field names. The
     * framework will resolve the metadata objects when necessary.
     *
     * @param tablePath Path of the table
     * @param fieldNames The field names need read
     * @return The requested table
     * @throws CatalogException in case of any runtime exception
     */
    default CatalogTable getTable(TablePath tablePath, List<String> fieldNames)
            throws TableNotExistException {
        return null;
    }

    /**
     * Create a new table in this catalog.
     *
     * @param tablePath Path of the table
     * @param table The table definition
     * @param ignoreIfExists Flag to specify behavior when a table with the given name already exist
     * @throws TableAlreadyExistException thrown if the table already exists in the catalog and
     *     ignoreIfExists is false
     * @throws CatalogException in case of any runtime exception
     */
    void createTable(TablePath tablePath, CatalogTable table, boolean ignoreIfExists, QualifiersModel qualifiersModel, TaskConf taskConf)
            throws TableAlreadyExistException, CatalogException, CreateTableException, DatabaseNotExistException;

    String getCreateTableSql(TablePath tablePath, CatalogTable table, TaskConf taskConf) throws DataTypeMappingNotExistException;

    /**
     * Drop an existing table in this catalog.
     *
     * @param tablePath Path of the table
     * @param ignoreIfNotExists Flag to specify behavior when a table with the given name doesn't
     *     exist
     * @throws TableNotExistException thrown if the table doesn't exist in the catalog and
     *     ignoreIfNotExists is false
     * @throws CatalogException in case of any runtime exception
     */
    void dropTable(TablePath tablePath, boolean ignoreIfNotExists)
            throws TableNotExistException, CatalogException;

    void createIndex(TablePath tablePath, CatalogTable table, boolean ignoreIfNotExists, TaskConf taskConf)
            throws IndexAlreadyExistException, CreateIndexException;

    void createForeignKey(TablePath tablePath, CatalogTable table, boolean ignoreIfNotExists, TaskConf taskConf) throws CatalogException;

    /**
     * 获取创建外键的SQL语句
     * @param tablePath
     * @param table
     * @return
     */
    List<String> getCreateForeignSqls(TablePath tablePath, CatalogTable table, TaskConf taskConf);


    /**
     * 获取创建索引的SQL语句
     * @param tablePath
     * @param table
     * @return
     */
    List<String> getCreateIndexSqls(TablePath tablePath, CatalogTable table, TaskConf taskConf);

    /**
     * 列出指定数据库or模式名下的所有视图名称
     * @param databaseName
     * @param schemaName
     * @return
     */
    List<String> listViewNames(String databaseName, String schemaName) throws CatalogException;

    /**
     * 出指定数据库or模式名下的所有视图信息
     *
     * @param databaseName
     * @return
     */
    List<ViewInfoModel> listViewsInfo(String databaseName, String schemaName) throws CatalogException;

    /**
     * 列出指定数据库or模式名下的所有存储过程名称
     * @param databaseName
     * @param schemaName
     * @return
     */
    List<String> listProcedureNames(String databaseName, String schemaName) throws CatalogException;

    /**
     * 列出指定数据库or模式名下的所有存储过程信息
     * @param databaseName
     * @return
     */
    List<ProcedureInfoModel> listProceduresInfo(String databaseName, String schemaName) throws CatalogException;

    /**
     * 列出指定数据库or模式名下的所有触发器名称
     * @param databaseName
     * @return
     */
    default List<String> listTriggerNames(String databaseName, String schemaName) throws CatalogException {
        return null;
    }

    /**
     * 列出指定数据库or模式名下的所有索引名称
     * @param databaseName
     * @return
     */
    default List<IndexInfo> listIndexInfos(String databaseName, String schemaName) throws CatalogException {
        return null;
    }

    /**
     * 列出指定数据库or模式名下的所有触发器信息
     * @param databaseName
     * @return
     */
    default List<TriggerInfoModel> listTriggersInfo(String databaseName, String schemaName) throws CatalogException {
        return null;
    }

    /**
     * 列出指定数据库or模式名下的所有类型名称
     * @param databaseName
     * @return
     */
    default List<String> listTypeNames(String databaseName, String schemaName) throws CatalogException {
        return null;
    }

    /**
     * 列出指定数据库or模式名下的所有类型信息
     * @param databaseName
     * @return
     */
    default List<TypeInfoModel> listTypesInfo(String databaseName, String schemaName) throws CatalogException {
        return null;
    }

    /**
     * 列出指定数据库or模式名下的所有序列名称
     * @param databaseName
     * @param schemaName
     * @return
     * @throws CatalogException
     */
    default List<String> listSeqNames(String databaseName, String schemaName) throws CatalogException {
        return null;
    }

    /**
     * 列出指定数据库or模式名下的所有函数名称
     * @param databaseName
     * @param schemaName
     * @return
     * @throws CatalogException
     */
    default List<String> listFunctionNames(String databaseName, String schemaName) throws CatalogException {
        return null;
    }

    /**
     * 列出指定数据库or模式名下的所有同义词名称
     * @param databaseName
     * @param schema
     * @return
     * @throws CatalogException
     */
    default List<String> listSynonymNames(String databaseName, String schema) throws CatalogException {
        return null;
    }
    /**
     * 列出指定名称的表的创建语句 DDL
     * @param tablePath
     * @return
     * @throws ViewNotExistException
     */
    String showCreateTable(TablePath tablePath) throws TableNotExistException;

    /**
     * 列出指定名称的视图的创建语句 DDL
     * @param tablePath
     * @return
     * @throws ViewNotExistException
     */
    String showCreateView(TablePath tablePath) throws ViewNotExistException;

    /**
     * 列出指定名称的存储过程的创建语句 DDL
     * @param tablePath
     * @return
     */
    String showCreateProcedure(TablePath tablePath) throws ProcedureNotExistException;

    /**
     * 列出指定名称的触发器的创建语句 DDL
     * @param tablePath
     * @return
     */
    String showCreateTrigger(TablePath tablePath) throws TriggerNotExistException;

    /**
     * 列出指定名称的类型的创建语句 DDL
     * @param tablePath
     * @return
     */
    String showCreateType(TablePath tablePath) throws TypeNotExistException;

    /**
     * 列出指定名称的序列的创建语句 DDL
     * @param tablePath
     * @return
     */
    String showCreateSeq(TablePath tablePath) throws SequenceNotExistException;

    /**
     * 列出指定名称的函数的创建语句 DDL
     * @param tablePath
     * @return
     */
    String showCreateFunc(TablePath tablePath) throws FunctionNotExistException;

    /**
     * 列出指定名称的索引的创建语句 DDL
     * @param tablePath
     * @return
     */
    String showCreateIndex(TablePath tablePath) throws IndexNotExistException;

    /**
     * 列出指定名称的同义词的创建语句 DDL
     * @param tablePath
     * @return
     */
    String showCreateSynonym(TablePath tablePath) throws SequenceNotExistException;

    /**
     *  返回带有database获取schema前缀的完整的表名称，前后都用数据库特定的quote包围起来
     * @param tablePath
     * @return
     */
    String getFullTableName(TablePath tablePath);

    /**
     * 各种数据库表名、字段名默认使用的引用。每个库一定要重载这个方法
     * @return
     */
    String getQuote();

    /**
     * 数据库、SCHEMA、表名、字段名是否大小写敏感？
     * @return
     */
    boolean caseSensitive();

    /**
     * 针对同一段SQL，查找源库和目标库不兼容的函数
     * @param sql 待检查的SQL
     * @param sourceCatalog 源库类型，取值参考{@link belf.migrate.api.common.DatabaseIdentifier}
     * @param sinkCatalog 目标库类型，取值参考{@link belf.migrate.api.common.DatabaseIdentifier}
     * @return
     */
    List<String> findFunctionIncompatible(String sql, String sourceCatalog, String sinkCatalog);

    /**
     * 查找指定表的unique约束。注意，不是查找唯一索引，是查找取值具有唯一性的字段。在创建表和迁移后的验证比较时用到这个方法。
     * @param tablePath
     * @return
     * @throws CatalogException
     */
    List<UniqueKey> getUniqueKeys(TablePath tablePath) throws CatalogException;

    /**
     * 查找表的Check约束。在创建表和迁移后的验证比较时用到这个方法。
     * @return
     * @throws CatalogException
     */
    List<CheckConstraint> getCheckConstraints(TablePath tablePath) throws CatalogException;

    /**
     * 删除指定视图
     * @param viewPath
     * @return
     * @throws CatalogException
     */
    default boolean dropView(TablePath viewPath) throws CatalogException {
        return false;
    }

    /**
     * 删除指定触发器
     * @param triggerPath
     * @return
     * @throws CatalogException
     */
    default boolean dropTrigger(TablePath triggerPath) throws CatalogException {
        return false;
    }

    /**
     * 删除指定类型TYPE
     * @param typePath
     * @throws CatalogException
     */
    default void dropType(TablePath typePath) throws CatalogException {}

    /**
     * 删除指定序列
     * @param seqPath
     * @throws CatalogException
     */
    default void dropSequence(TablePath seqPath) throws CatalogException {}

    /**
     * 删除指定函数
     * @param functionPath
     * @throws CatalogException
     */
    default void dropFunction(TablePath functionPath) throws CatalogException {}

    /**
     * 删除指定同义词synonym
     * @param synonymPath
     * @throws CatalogException
     */
    default void dropSynonym(TablePath synonymPath) throws CatalogException {}

    /**
     * 删除指定存储过程
     * @param procedurePath
     * @throws CatalogException
     */
    default void dropProcedure(TablePath procedurePath) throws CatalogException {}

    /**
     * 获取索引信息。一般每种数据库需要定义自己个性化的索引信息获取方法。如果不定义，默认调用{@link CatalogUtils#getIndexKeys(DatabaseMetaData, TablePath)}方法
     * @param tablePath 要访问的表
     * @return 每个Catalog实现如果不提供个性化方法，默认该方法返回null
     */
    default List<IndexKey> getIndexKeys(TablePath tablePath) throws TableNotExistException, CatalogException {
        return null;
    }

    /**
     * 指定表是否存储数据？即行数非零
     * @param tablePath
     * @return
     * @throws CatalogException
     */
    default boolean isExistsData(TablePath tablePath) throws CatalogException {
        return false;
    }

    /**
     * 查询指定表的行数
     * @param viewPath
     * @return
     * @throws CatalogException
     */
    default String countDataForTable(TablePath viewPath) throws CatalogException {
        return null;
    }

    /**
     * 根据表路径生成获取表中数据总量的SQL语句。
     *
     * @param tablePath 表的路径信息，用于构建SQL语句中的表名部分。
     * @return 返回生成的SQL语句，用于查询指定表中的数据计数。默认情况下返回null，表示未实现或不需要生成此类SQL语句。
     */
    default String getCountDataSql(TablePath tablePath) {
        return null;
    }

    /**
     * 执行指定的SQL语句
     * @param tablePath
     * @param sql
     * @throws CatalogException
     */
    default void executeSql(TablePath tablePath, String sql) throws CatalogException {}


    /**
     * 通用执行sql接口，可获取结果集
     * @param tablePath
     * @param sql
     * @return
     * @throws SQLException
     */
    default List<JdbcExecuteResult> execute(TablePath tablePath, String sql) throws SQLException {
        return null;
    }

    /**
     * 对指定表，在插入数据前关闭自增属性
     * @param tablePath
     */
    default void autoIncreamentOff(TablePath tablePath, Connection connection) {}

    /**
     * 对指定表，在数据插入后恢复自增属性
     * @param tablePath
     * @param connection
     */
    default void autoIncreamentOn(TablePath tablePath, Connection connection) {}

    /**
     * 通过查询系统表获取指定表的所有列信息
     * @param tablePath
     * @return
     * @throws CatalogException
     * @throws TableNotExistException
     */
    default List<ColumnMetaData> getAllColumnsBySql(TablePath tablePath) throws CatalogException, TableNotExistException {
        return null;
    }

    /**
     * 特殊字段类型列表
     * @return
     */
    default List<String> specialColumns() {
        return null;
    }

    /**
     * 查询所有大数据字段类型列表
     * @return
     */
    default List<String> bigDataColumns() {
        return null;
    }

    /**
     * 列出数据库中当前用户账号下的自定义类型的名字。一些数据库如Oracle、达梦是支持自定义类型的；MySQL不支持类型，无需重载该方法
     * @param schema 类型所属schema
     * @param typeNames 限定要返回的TYPE的名字
     * @return 返回所有的自定义Type的名字
     */
    default List<UserDefType> listTypes(String schema, List<String> typeNames) throws CatalogException {
        return null;
    }

    /**
     * 列出数据库中指定用户下的序列。
     * @param owner 指定用户（或者说模式）
     * @param sequenceNames 指定的序列名称
     * @return 返回满足条件的序列对象
     * @throws CatalogException
     */
    default List<UserDefSeq> listSequences(String owner, List<String> sequenceNames) throws CatalogException {
        return null;
    }

    /**
     * 列出数据库中指定用户下的函数。
     * @param schema 指定数据库（或者模式）
     * @param functionNames 指定的函数名称
     * @return 返回满足条件的函数对象
     * @throws CatalogException
     */
    default List<UserDefFunction> listFunctions(String schema, List<String> functionNames) throws CatalogException {
        return null;
    }

    /**
     * 列出数据库中指定用户下的同义词
     * @param schema 指定模式
     * @param synonymNames 指定的同义词名称
     * @return 返回满足条件的函数对象
     * @throws CatalogException
     */
    default List<UserDefSynonym> listSynonyms(String schema, List<String> synonymNames) throws CatalogException {
        return null;
    }

    /**
     * 使用JDBC中{@link java.sql.Statement#executeUpdate(String)}执行DDL操作
     * @param tablePath 要修改的数据库对象，可以是表、视图、触发器、类型等
     * @param ddl 要执行的DDL语句
     * @throws DDLException
     */
    default void executeDdl(TablePath tablePath, String ddl) throws DDLException {
        return;
    }

    /**
     * 查询指定TYPE的所有字段名称并返回
     * @param owner 指定用户（或者说模式）
     * @param typeName 类型TYPE名称
     * @return 自定义类型的所有字段名称
     * @throws CatalogException
     */
    default List<String> listTypeColumns(String owner, String typeName) throws CatalogException {
        return null;
    }

    /**
     * 比较源端和目标端两个字段的数据类型是否一样。不能完全从字面意思比较，需要检查两个字段是否可以互相转换
     * @param sourceType 源端字段类型
     * @param sinkType 目标段字段类型
     * @return
     */
    default boolean columnTypeEqual(String sourceType, String sinkType) {
        return false;
    }

    /**
     * 将源端和目标端字段转为指定类型的值进行比较，比较结果是否一致
     * @param type
     * @param sourceData
     * @param sinkData
     * @return
     */
    boolean dataEqualByType(String type, Object sourceData, Object sinkData);

    /**
     * 设置当前数据库。这个方法是否起作用，需要实际应用中进行检验
     * @param connection
     * @param database
     * @throws SQLException
     */
    void setCurrentDatabase(Connection connection, String database) throws SQLException;

    /**
     * 设置数据库会话当前schema。这个方法是否起作用，需要实际应用中进行检验
     * @param connection
     * @param schema
     * @throws SQLException
     */
    void setCurrentSchema(Connection connection, String schema) throws SQLException;

    /**
     * 获取删除表的DDL语句
     * @param tablePath
     * @return
     */
    String getDropTableSql(TablePath tablePath);

    /**
     * 获取删除视图的DDL语句
     * @param viewPath
     * @return
     */
    String getDropViewSql(TablePath viewPath);

    /**
     * 获取删除触发器的DDL语句
     * @param triggerPath
     * @return
     */
    String getDropTriggerSql(TablePath triggerPath);

    /**
     * 获取删除自定义类型的DDL语句
     * @param typePath
     * @return
     */
    String getDropTypeSql(TablePath typePath);

    /**
     * 获取删除序列的DDL语句
     * @param seqPath
     * @return
     */
    String getDropSequenceSql(TablePath seqPath);

    /**
     * 获取删除函数的DDL语句
     * @param functionPath
     * @return
     */
    String getDropFunctionSql(TablePath functionPath);

    /**
     * 获取删除同义词的DDL语句
     * @param synonymPath
     * @return
     */
    String getDropSynonymSql(TablePath synonymPath);

    /**
     * 获取删除存储过程的DDL语句
     * @param procedurePath
     * @return
     */
    String getDropProcedureSql(TablePath procedurePath);

    /**
     * 是否支持审计日志。
     * @return
     */
    boolean isAuditLog();

    /**
     * 获取源数据库的一些特性
     */
    List<CharacteristicInfo> getCharacteristicInfo(String sql);

    /**
     * 去掉某些特定数据库特定sql结尾的分号
     */
    String trimSql(String sql);

    String[] getScriptDelimiters();

    String[][] getBlockBoundStrings();
}
